
#include "khan.hpp"
#include "command_manager.hpp"

command_manager_ptr command_manager::_instance;
boost::mutex command_manager::_singletone_lock;

command_manager_ptr 
command_manager::instance() 
{
    if (_instance.use_count() == 0) {
        boost::mutex::scoped_lock scoped_lock(_singletone_lock);
        if (_instance.use_count() == 0) 
            _instance.reset(new command_manager());
    }
    return _instance;
};

void command_manager::show_agent_runcfg(vty_ptr vty)
{
	int ret = 0;
    string_vector req_opts;
    cmd_mng_session_ptr agent;
    agent_map_iterator  iter;
    vty_ipc_message_t msg;

	//boost::mutex::scoped_lock scoped_lock(_agent_lock);		/* FIXME */
	iter = _agent_map.begin();

	for (; iter != _agent_map.end(); iter++) {
		agent.reset(new cmd_mng_session(_ioserv_pool.get_io_service(), (*iter).second));

		if (agent.use_count() == 0) {
			vty->output("$$ interanl error !!!!%s", VTY_NEWLINE);
			continue;
		};

		_ipcmsg.build_msg(vty->id(), VTY_RUN_CFG, VTY_RET_NONE, req_opts, msg);

		agent->connect();
		if (!agent->send_msg(msg)) { ret = -1; }

		ret = handle_sub_remote_function(vty, agent);
	}
}

command_manager::command_manager():
    _ioserv_pool(4),    _sid(0),
    _banner("wellcome to kauri nos.\n"),
    _passwd("newdeea"), _cmdmng(cmd_mng::instance())
{
    char    buf[128];
    if (gethostname(buf, 128) < 0)
        throw std::runtime_error("syscall failed");
    _hostname = buf;
};

io_session_ptr command_manager::create_session(io_service& ioserv) 
{
    cmd_db_worker_ptr worker(
            new cmd_db_worker(_sid, shared_from_this()));
    ipc_io_session_ptr ptr(
            new ipc_session_t(_sid, ioserv, *this, worker));

	/* FIXME: check agent session  is unique !!! */
	_worker_map[ptr] = worker;

    PDBG("%p worker number: %d\n", this, (int)_worker_map.size());
    _sid++;
    return ptr;
}

void command_manager::leave(io_session_ptr participant)
{
    PDBG("%p size: %d\n",this,  (int)_worker_map.size());
    session_map_iter iter = _worker_map.begin();
    for ( ; iter != _worker_map.end(); iter++) {
        PDBG(">> id: %d\n", (*iter).first->id());
    };
    PDBG("id: %d\n", participant->id());
    iter = _worker_map.find(participant);
    if (iter == _worker_map.end()) {
        PDBG("don't find... \n");
    } else {
        _worker_map.erase(iter);
        PDBG("erase session and worker\n");
    }
}


bool command_manager::add_command( const std::string mode, const std::string cmd, 
        const std::string help, const tcp_endpoint& rep, __u64 agent_id, __u64 function_id)
{
    bool ret = false;
    agent_map_iterator  iter;

    {
        boost::mutex::scoped_lock scoped_lock(_agent_lock);
        iter = _agent_map.find(agent_id);
        if (iter == _agent_map.end()) {	/* FIXME: update cmd */
            _agent_map[agent_id] = rep;
        } 
    }

    ret = this->add_command(mode, cmd, help, 
            boost::bind( &command_manager::handle_remote_function,
                shared_from_this(), agent_id, function_id, _1, _2, _3));
    return true;
}


int command_manager::handle_remote_function(__u64 agent_id, __u64 function_id, 
        vty_ptr vty, const std::string& cmdline, string_vector& opts)
{
    int ret = 0;
    char buf[256];
    string_vector req_opts;
    tcp_endpoint    rep;
    cmd_mng_session_ptr agent;
    agent_map_iterator  iter;
    vty_ipc_message_t msg;

    {
        boost::mutex::scoped_lock scoped_lock(_agent_lock);
        iter = _agent_map.find(agent_id);
        if (iter != _agent_map.end()) 
            agent.reset(new cmd_mng_session(_ioserv_pool.get_io_service(), (*iter).second));
    }

    if (agent.use_count() == 0) {
        vty->output("$$ interanl error !!!!%s", VTY_NEWLINE);
        return  ret;
    };

    PDBG("vty id: %d, function id (0x%llx)\n", vty->id(), function_id);
    sprintf(buf, "%llX", function_id);
    req_opts.push_back(buf);
    req_opts.insert(req_opts.end(), opts.begin(), opts.end());

    _ipcmsg.build_msg(vty->id(), VTY_EXEC_FUNC, VTY_RET_NONE, req_opts, msg);

    agent->connect();
    if (!agent->send_msg(msg)) { ret = -1; }

    PDBG("why do you here!!!!!!!!?\n");
    ret = handle_sub_remote_function(vty, agent);

    return 0;
};


int command_manager::handle_remote_dialog_function(cmd_mng_session_ptr agent, 
        vty_ptr vty, const std::string line)
{
    string_vector opts;
    vty_ipc_message_t msg;

    opts.push_back(line);
    _ipcmsg.build_msg(vty->id(), VTY_DIAG_MSG, VTY_RET_NONE, opts, msg);
    PDBG(">>>>>>>>>>>>. BEGIN\n");
    if (agent->connect() != true) { /* FIXME */
        vty->output("$$ fail to connect remote agent!!%s", VTY_NEWLINE);
        vty->stop_dialog();
        return 0;
    }
    PDBG(">>> SEND VTY_DIAG_MSG!!!\n");
    if (agent->send_msg(msg) != true) { /* FIXME */
        vty->output("$$ fail to send msg to remote agent!!%s", VTY_NEWLINE);
        vty->stop_dialog();
        return 0;
    }
    handle_sub_remote_function(vty, agent);
    PDBG("<<<<<<<<<<<<< END\n");
    return 0;
}

int command_manager::handle_remote_start_dialog(cmd_mng_session_ptr agent, vty_ptr vty, string_vector& opts)
{
    agent->disconnect();
    PDBG("START DIALOG\n");
    return  vty->start_dialog(
            boost::bind( &command_manager::handle_remote_dialog_function,
                shared_from_this(), agent, _1, _2));
}

int command_manager::handle_sub_remote_function(vty_ptr vty, cmd_mng_session_ptr agent)
{
    __u32 vty_id, cmd, ret = VTY_SUCC;
    string_vector opts, resp_opts;
    vty_ipc_message_t msg;

    while (1) {

        PDBG("....................................\n");
        if (agent->recv_msg(msg) != true) {
            return -1;
        }

        _ipcmsg.parse_msg(msg, vty_id, cmd, ret, opts);

        if (cmd == VTY_DIAG_MSG) {
            PDBG("END DIAG TEMP SESSION\n");
            agent->disconnect();
            return 0;
        }
		if (cmd == VTY_RUN_CFG) {
            PDBG("CLOSE RUN_CFG SESSION!!!!!!!!!!!!!!!!");
            agent->disconnect();
		}
        if (cmd == VTY_EXEC_FUNC) {
            PDBG("CLOSE CMD SESSION!!!!!!!!!!!!!!!!");
            agent->disconnect();
            return (ret == VTY_SUCC) ? 0 : -1; 
        }
        if (cmd == VTY_STOP_DIAG) {
            PDBG("CLOSE DIALOG SESSION!!!!!!!!!!!!!!!!");
            vty->stop_dialog(); 
            _ipcmsg.build_msg(vty_id, cmd, ret, resp_opts, msg);
            if (agent->send_msg(msg) != true) {
                ret = VTY_FAIL;
				break;
            }
            agent->disconnect();
            return (ret == VTY_SUCC) ? 0 : -1; 
        }

        PDBG(">>>>>>>>> recv cmd: %d, opt num: %d\n", cmd, (int)opts.size());
        if (cmd == VTY_START_DIAG) {
            PDBG(">>>>>>>>> RECV VTY_START_DIAG\n");
        }
        switch (cmd) {
            case VTY_IS_AUTH_MODE:
                if (opts.empty()) 
                    ret = (vty->is_auth_mode() == true) ? VTY_SUCC : VTY_FAIL;
                else 
                    ret = (vty->is_auth_mode(opts[0]) == true) ? VTY_SUCC : VTY_FAIL;
                break;
            case VTY_SET_BANNER:     
                if (opts.empty()) { ret = VTY_FAIL; break; }
                vty->set_banner(opts[0]);
                break;
            case VTY_GET_BANNER:     
                resp_opts.push_back(vty->get_banner());
                break;
            case VTY_CHECK_PW:
                if (opts.empty()) { ret = VTY_FAIL; break; }
                else 
                    ret = (vty->is_auth_mode(opts[0]) == true) ? VTY_SUCC : VTY_FAIL;
                break;
            case VTY_CUR_MODE:       
                resp_opts.push_back(vty->current_mode());
                PDBG(">>>>> check: %s\n", resp_opts[0].c_str());
                ret = VTY_SUCC;
                break;
            case VTY_CHG_MODE: 
                if (opts.empty()) { ret = VTY_FAIL; break; }
                else 
                    ret = (vty->change_mode(opts[0]) == true) ? VTY_SUCC : VTY_FAIL;
                break;
            case VTY_IS_DIAG:
                ret = (vty->is_dialog() == true) ? VTY_SUCC : VTY_FAIL;
                break;
            case VTY_START_DIAG:    /* FIXME */
                handle_remote_start_dialog(agent, vty, opts);
                break;
            /*case VTY_DIAG_MSG:
                break;*/
            case VTY_EXEC_PTY:       
                break;
            case VTY_OUTPUT: 
                if (opts.empty()) { ret = VTY_FAIL; break; }
                vty->output(opts[0]);
                break;
            default:
                ret = VTY_FAIL;
                break;
        }

        /* FIXME */
        if (cmd == VTY_START_DIAG || cmd == VTY_EXEC_FUNC) continue;

        PDBG("cmd %d ret %d\n", cmd, ret);
        _ipcmsg.build_msg(vty_id, cmd, ret, resp_opts, msg);
        if (agent->send_msg(msg) != true) {
            return -1;
        }
        ret = VTY_SUCC;
        opts.clear();
        resp_opts.clear();
    }

    return ret;
}

